iT邦幫忙

2021 iThome 鐵人賽

DAY 26
0
Software Development

一個新鮮人如何完轉Spring boot與DevOps從0到101系列 第 26 篇

使用 OpenTelemetry api 自訂義內容

  • 分享至 

  • xImage
  •  

Resources

在 OpenTelemetry 中,服務由資源描述,資源是在應用程式啟動期間初始化 OpenTelemetry SDK 時設置的。詳細規範可點擊此鏈結。

Attribute Description Example Required
service.name 給予服務的額外名稱。對於水平擴展服務的所有實例,必須相同 oteldemo Y
service.version 定義服務 API 或服務版本 aip:1.2.3 N
host.hostname 主機名或以域名表示 oteldemo.com.tw N

目前只能使用環境變數方式進行設置 OTEL_RESOURCE_ATTRIBUTES。這些的設定都是為了讓後端能夠進一步的識別分析。

Span

Span 是組成 Tracer 的單位。

SpanContext

為了要有完整 Tracer 的過程,每個 Span 要將 SpanContext 傳遞至子 Span。

SpanContext 透過 span_ID 告知子 span 誰是父,以及它屬於什麼 Tracer(trace_ID)。

Attributes

由 Key-value 組成,提供 Span 詳細的資訊。可以查詢、分組或以其他方式分析 trace 和 span。

Status

可將其設定為 OK、Cancelled 或 Permission Denied。

SpanKind

SpanKind 在 Tracer 中提供有用訊息,此 Span 是否為遠端系統? SpanKind 值可以為 CLIENT、SERVER、PRODUCER、CONSUMER 和 INTERNAL。詳細可參考官方鏈結

Events

包含名稱、時間戳和可選的 Attributes 集合等。其表示在 Span 工作負載內的特定時間發生所發生的事件。可能如下

events: 
t:3
name:log
message: "retrieved 400 records"
...

程式碼實現概念

Annotation @WithSpan

每次調用帶註釋的方法時,它會在當前 Trace 中創建一個子 `Span,並記錄所有引發的異常。

也可以透過 exclude,排除一些不要的場景。

Accessing the tracer

如果要與 Tracer 進行交互,需要先獲取 Tracer 的實例。Tracer 以它們要檢測的組件命名。通常是一個 library、一個 package或一個 class。

Tracer tracer = GlobalOpenTelemetry.getTracer(instrumentationName, instrumentationVersion);

Span 屬性使用

  • Span 中 setAttribute 是用於細分數據的索引。可能想要新增帳戶 ID 之類,以了解請求瓶頸和錯誤是否特定於某個帳戶或是否影響每個帳戶。
  • Span 中 addEvent 可將細節做記錄,就是日誌的概念。這可自動查找與特定事務關聯的所有日誌,而不必進行大量的搜索和過濾。隨著服務的擴展這將是一個很好的追蹤過程

Accessing the current span

Span 是在應用程式框架中被創建和管理。每個請求自動創建一個 Tracer,且應用程式程式碼已經被包裝在一個 Span,可用於新增特定於應用程式的屬性(setAttribute)和事件(addEvent)。

上述的前提是框架要被支援。

Span span = Span.current()

Create own spans

當創建自己的 Span。這些範圍將自動成為當前範圍的子,同時新增至 Tracer 中。Span 管理包括三個步驟:開始 Span、設置為當前 Span 和結束 Span。當前如果存在 Span,OpenTelemetry 會將其創建為當前 Span 的子。會在 Tracer 上調用 spanBuilder 方法以觸發一個新的 Tracer。創建新的 Span 後,使用 Scope 建立一個新的代碼區塊,當中會包含子 Span。在該範圍內對 Span.current() 的任何調用都將回傳該子 Span,而不是請求的父 Span。都完成後,需呼叫 end() 方法來關閉 Span,否則會有洩漏問題。

Error Handling

在 OpenTelemetry 中,異常記錄為事件。但是,為確保異常的格式正確,應使用 span.recordException(error) 方法而非 addEvent。

childSpan.recordException(new RuntimeException("oops"));
childSpan.setStatus(StatusCode.ERROR);

在 OpenTelemetry 中,錯誤表示整體操作未完成。當異常被處理並不表示著整個操作都無法完成。為了示意操作失敗,需調用 span.setStatus() 並傳入錯誤代碼,該代碼可讓分析工具來自動觸發警報、測量錯誤率等。

實作

使用 Annotation 方式,此方式會建立一個 Span,而此 Span 會是觸發該 Span 的子 Span,我們這邊設置了屬性(setAttribute)和事件(event)這會以 Log 方式呈現。

    @WithSpan
    public static void requestValueSpan(Note note) {
        Span span = Span.current();
        span.setAttribute("id", note.getId());
        span.addEvent("Get.note.from.id", Attributes.of(AttributeKey.stringKey("content"), note.getContent(),AttributeKey.stringKey("title"), note.getTitle()));
    }

下面算是實作 @WithSpan 的方式,只不過需要定義一個 Tracer,在建立一個 Span(spanBuilder)。

private final Tracer tracer = GlobalOpenTelemetry.getTracer(NoteServiceImp.class.getSimpleName());
    @Override
    public void saveNote(Note note) {
        // TODO Auto-generated method stub
        Span span = tracer.spanBuilder("saveNote").startSpan();
        try (Scope scope = span.makeCurrent()) {
            this.noteRepository.save(note);
            span.setAttribute("CreateTime", note.getCreatedAt().toString());
            span.addEvent("Request.query", Attributes.of(AttributeKey.stringKey("content"), note.getContent(),AttributeKey.stringKey("title"), note.getTitle()));
        } catch (Throwable t) {
            span.setStatus(StatusCode.ERROR, "Change it to your error message");
        } finally {
            span.end(); // closing the scope does not end the span, this has to be done manually
        }
    }

建立父子關係的 Span,在 child 方法中呼叫 setParent。

    private static void parent() {
        Span parentSpan = tracer.spanBuilder("parent").startSpan();
        try {
            parentSpan.addEvent("parent", Attributes.of(AttributeKey.stringKey("parent"), "parent"));
            child(parentSpan);
        } finally {
            parentSpan.end();
        }
    }

    private static void child(Span span) {
        Span childSpan = tracer.spanBuilder("child").setParent(Context.current().with(span)).startSpan();
        try (Scope scope = childSpan.makeCurrent()) {
            childSpan.setAttribute("name", "child");
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            childSpan.end();
        }
    }

設定 optntelemtry agent

架設開發環境在 VSCODE,需在 lunch.json 中設定 vmArgs,定義與前幾章 docker-compose 中幾乎相同的參數,-javaagent:opentelemetry-javaagent-all.jar 導入 opentelemetry,定義 endpoint。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "java",
            "name": "Launch Current File",
            "request": "launch",
            "mainClass": "${file}",
            "vmArgs": "-javaagent:opentelemetry-javaagent-all.jar -Dotel.resource.attributes=service.name=oteldemo -Dotel.exporter.otlp.endpoint=http://172.17.10.105:4317 -Dotel.exporter.otlp.traces.endpoint=http://172.17.10.105:4317 -Dotel.exporter.otlp.metrics.endpoint=http://172.17.10.105:4317"
        },
        {
            "type": "java",
            "name": "Launch OteldemoApplication",
            "request": "launch",
            "mainClass": "com.otel.example.oteldemo.OteldemoApplication",
            "projectName": "oteldemo"
        }
    ]
}

上述的範例可參考 github


上一篇
OpenTelemetry 與 Jaeger 應用 - 2
下一篇
Spring boot 與 MongoDB 之連線
系列文
一個新鮮人如何完轉Spring boot與DevOps從0到101 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言